fs = require("fs")
path = require('path')
webpack = require("webpack")
CopyWebpackPlugin = require "copy-webpack-plugin"
HtmlWebpackPlugin = require('html-webpack-plugin')
MiniCssExtractPlugin = require("mini-css-extract-plugin")
#ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
TerserPlugin = require('terser-webpack-plugin')
{VueLoaderPlugin} = require('vue-loader')
PreloadWebpackPlugin = require('@vue/preload-webpack-plugin')

merge = require 'lodash/merge'
require './webpack/require'
require './webpack/urlbase64'


production = "production"
development = "development"


read = (fp, default_value)=>
  if fs.existsSync(fp)
    return fs.readFileSync(fp, 'utf8').trim()
  return default_value


module.exports = (pwd, config)=>
  config = config or {}

  IS_DEV = process.env.NODE_ENV == development

  if IS_DEV
    NAME = '[hash:urlbase64:8]/[name].[ext]?[path]'
  else
    NAME = '[hash:urlbase64:8].[ext]'


  OUTPUT_PATH = config.output?.path or path.join(pwd, "dist")
  SRC = path.join(pwd, "src")+'/'

  publicPath = read SRC+'config/cdn.txt','/'

  plugins = []


  ENTRY = config.entry or {}
  delete config.entry
    #"sw":SRC+"coffee/sw.coffee"
  for k,v of ENTRY
    ENTRY[k] = SRC+v

  ALIAS = {
    # '@vue': '@vue/composition-api'
    "~" : SRC[..-1]
  }

  rules = [
    {
      test: /\.txt$/i
      use: 'trim-raw-loader'
    }
    {
      test: /\.pug$/
      oneOf: [
        {
          resourceQuery: /^\?vue/,
          use: ['pug-plain-loader']
        }
        {
          use: ['pug-loader']
        }
      ]
    }
    {
      test: /\.m?js$/
      exclude: /(node_modules|bower_components)/
      resolve:
        fullySpecified: false
      use: [
        'babel-loader'
        {
          loader:'source-map-loader'
          options:
            filterSourceMappingUrl: (url, resourcePath) =>
              if /.*\/node_modules\/.*/.test(resourcePath)
                  return false
              return true
        }
      ]
    }
    {
      test: /\.coffee$/
      use: [
        {
          loader:'coffee-loader'
          options:{
            transpile: {
                presets: [
                  [
                    "@babel/preset-env",
                    {
                      modules: false
                    }
                  ]
                ]
                plugins: [
                  "@babel/plugin-transform-runtime"
                  # [
                  #   "import"
                  #   {
                  #     libraryName: "ant-design-vue"
                  #     libraryDirectory: "es"
                  #     style:true
                  #   }
                  # ]
                  # [
                  #   "component",
                  #   {
                  #     "libraryName": "element-ui",
                  #     "styleLibraryName": "~src/styl/element/theme"
                  #   }
                  # ]
                ]
              }
          }
        }
      ]
    }
    {
      test: /\.svg/
      use:
        loader: 'svg-url-loader'
        options:
          name:NAME
          limit: 2048
    }
    {
      test:/\.(avif|png|jpg|gif|webp)$/
      loader:'url-loader'
      options:{
        limit:10000
        name:NAME
      }
    }
    {
      test:/\.vue$/,
      loader:'vue-loader'
    }
    {
      test:/\.(woff2?|eot|ttf|otf)$/
      loader:"url-loader"
      options:
        limit:10000
        name:NAME
    }
  ]

  less_vars = SRC+'lib/less.vars.coffee'
  if fs.existsSync(less_vars)
    rules.push {
      test: /\.less$/i
      use:[
        {
          loader: "style-loader"
        }
        {
          loader: "css-loader"
        }
        {
          loader:'less-loader'
          options:
            lessOptions:
              modifyVars:require(less_vars)
              javascriptEnabled: true
        }
      ]
    }

  target = config.target or 'web'

  if target == "web"
    pkg_dir = SRC+"pkg"
    if fs.existsSync(pkg_dir)
      fs.readdirSync(pkg_dir).map (name) =>
        pkg = SRC+"pkg/#{name}"
        ALIAS["~"+name] = pkg
        title = pkg+'/config/title.txt'
        if not fs.existsSync title
          return
        ENTRY[name] = pkg+"/coffee/index.coffee"
        plugins.push new HtmlWebpackPlugin({
          template: SRC+'pug/index.pug'
          filename:"index.html"
          chunks:[name]
          inject:'body'
          title:require(title)
          meta:
            viewport: 'width=device-width,initial-scale=1,shrink-to-fit=no'
          minify:
            collapseWhitespace: true
            removeComments: true
            removeRedundantAttributes: true
            removeAttributeQuotes: true
            removeScriptTypeAttributes: true
            removeStyleLinkTypeAttributes: true
            useShortDoctype: true
        })
    plugins = plugins.concat [
      new webpack.ProvidePlugin({
        process: 'process/browser'
      })
      new webpack.ProvidePlugin({
        Buffer: ['buffer', 'Buffer']
      })
    ]
    if not IS_DEV
      plugins.push new PreloadWebpackPlugin({
        rel: 'prefetch'
        as: (entry) =>
          entry = entry.slice(entry.lastIndexOf(".")+1)
          switch entry
            when 'css'
              return 'style'
            when 'woff','woff2'
              return 'font'
            when 'jpg','gif','svg','png','webp','avif'
              return 'image'
          return 'script'
      })

  if target == 'web' or target == 'electron-renderer'

    {CleanWebpackPlugin} = require('clean-webpack-plugin')
    plugins = plugins.concat [
      new CleanWebpackPlugin()
      new VueLoaderPlugin()
      new CopyWebpackPlugin({
        patterns:[
          {
            from: path.resolve(pwd, 'static')
            to: OUTPUT_PATH
            globOptions:
              ignore: [
                '**/.git/**/*'
              ]
          }
        ]
      })
    ]

  if IS_DEV
    #exports.watch = true
    filename = "name"
  else
    filename = "contenthash:8"

  plugins.unshift new webpack.DefinePlugin({
    DEV:IS_DEV
    __VUE_OPTIONS_API__: true
    __VUE_PROD_DEVTOOLS__: false
  })

  filename = "[#{filename}].css"

  plugins.unshift new MiniCssExtractPlugin({
    filename
    chunkFilename:filename
  })

  exports = {
  target
  watchOptions:
    aggregateTimeout: 300
    poll: 1000
  entry:ENTRY
  performance:
    assetFilter: (assetFilename) =>
      if assetFilename.endsWith('.woff2')
        return false
      true
  plugins
  resolve:
    extensions: '.coffee .js .styl .css .vue .json .pug'.split(' ')
    alias:ALIAS
    fallback:
      fs:false
      crypto: false
      buffer:false
      path:require.resolve("path-browserify")
      stream: require.resolve("stream-browserify")
      assert: require.resolve("assert/")
  module:{
    rules
  }
  output: {
    publicPath
    path: OUTPUT_PATH
  }
  }

  if IS_DEV
    if target == "web" or target == "electron-renderer"
      {host,port} = config.devServer
      if not host
        host = "0.0.0.0"
        url_host = "127.0.0.1"
      else
        url_host = host

      exports = Object.assign exports, {
        mode:development
        devtool:'eval-cheap-module-source-map'
        devServer : {
          host
          port
          compress: true
          contentBase: OUTPUT_PATH
          disableHostCheck: true
          hot: true
          public : read(SRC+"config/http.txt", 'http')+"://"+url_host+":"+port
          historyApiFallback:
            disableDotRule: true
        }
      }
  else
    if target == "web"
      Object.assign(
        exports.output
        {
          chunkFilename: "[contenthash].js"
          filename: "[contenthash].js"
          hashDigest: "urlbase64"
          hashDigestLength: 8
          hashFunction: "sha3-256"
        }
      )
    OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
    Object.assign(
      exports
      {
        mode:production
        # devtool:'cheap-module-source-map'
        optimization:
          # Tree Shaking 终极优化指南 https://t.cn/A6vibt7P
          usedExports: true
          splitChunks:
            cacheGroups:
              styles:
                name: 'style'
                test: /\.css$/
                chunks: 'all'
                enforce: true
          minimizer: [
            new OptimizeCSSAssetsPlugin({})
            new TerserPlugin(
              parallel: true
              extractComments:false
              terserOptions:
                sourceMap: false
                compress:
                  # warnings:false,
                  drop_debugger:true
                  drop_console: true
                output:
                  comments: false
            )
          ]
      }
    )

  do =>
    use = [
        {
          loader: MiniCssExtractPlugin.loader
          options: {
            # hmr: IS_DEV
            publicPath
          }
        }
        {
          loader: 'css-loader'
          options:
            sourceMap: IS_DEV
        }

    ]
    rules.push {
      test: /\.css$/
      use
    }
    rules.push {
      test: /\.styl(us)?$/
      use : use.concat [
        {
          loader: 'postcss-loader'
          options:
            sourceMap: IS_DEV
            # config:
            #   path: 'postcss.config.js'
        }
        {
          loader: 'stylus-loader'
          options: sourceMap: IS_DEV
        }
      ]
    }
  exports = merge exports, config
  exports

